package demoMod.gdxform.ui;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import demoMod.gdxform.abstracts.Container;
import demoMod.gdxform.core.FormManager;
import demoMod.gdxform.interfaces.Element;
import demoMod.gdxform.interfaces.KeyboardEventSubscriber;
import demoMod.gdxform.interfaces.MouseEventSubscriber;

import java.util.function.Supplier;

public class GMenuItem implements Element, MouseEventSubscriber, KeyboardEventSubscriber {
    private String id = "";
    private Container<? extends Element> parent;
    private float y;
    private float width;
    private float height;
    private Color bgColor = Color.LIGHT_GRAY.cpy();
    private Color selectedBgColor = Color.BLUE.cpy();
    Color textColor = Color.BLACK.cpy();
    Color disabledTextColor = Color.DARK_GRAY.cpy();
    Color selectedTextColor = Color.WHITE.cpy();
    Texture bgTexture;
    Texture selectedBgTexture;
    Texture icon;
    float iconWidth = 16;
    private boolean enabled = true;
    private boolean visible = true;
    private boolean mouseDown = false;
    private boolean hasSplitLine = true;
    private boolean selected = false;
    private float hoveredTimer = 0.0F;
    private Color splitLineColor = Color.BLACK;
    Texture splitLineTexture;
    final GLabel label;
    final GLabel subMenuMarker;
    private int priority;
    Supplier<GMenuList> subMenuSupplier;
    private GMenuList subMenu;
    protected float subMenuWaitTimer = 0.3F;

    public GMenuItem(float width, float height, String text, BitmapFont font) {
        refreshBgTexture();
        refreshSelectedBgTexture();
        refreshSplitLineTexture();
        this.label = new GLabel(iconWidth, font.getLineHeight() / 2.0F, width, height, font);
        this.label.setText(text);
        this.label.setColor(textColor);
        this.label.setParent(null);
        this.subMenuMarker = new GLabel(width * 0.9F, font.getLineHeight() / 2.0F, Math.max(width * 0.1F, 16), height, font);
        this.subMenuMarker.setText(">");
        this.subMenuMarker.setColor(textColor);
    }

    @Override
    public String getId() {
        return id;
    }

    @Override
    public void setId(String id) {
        this.id = id;
    }

    public GMenuList getSubMenu() {
        GMenuList ret = subMenuSupplier.get();
        ret.setParentItem(this);
        return ret;
    }

    public void setSubMenu(Supplier<GMenuList> subMenuSupplier) {
        this.subMenuSupplier = subMenuSupplier;
    }

    @Override
    public void update() {
        this.label.setX(getX(true) + this.iconWidth);
        this.label.setY(getY(true) + this.label.getFont().getLineHeight() / 2.0F);
        this.subMenuMarker.setX(getX(true) + getWidth() * 0.8F);
        this.subMenuMarker.setY(getY(true) + this.subMenuMarker.getFont().getLineHeight() / 2.0F);
        if (selected) {
            hoveredTimer += Gdx.graphics.getDeltaTime();
        } else {
            hoveredTimer = 0.0F;
            if (this.subMenu != null) {
                this.subMenu.disappear();
                this.subMenu = null;
            }
        }
        if (hoveredTimer >= subMenuWaitTimer && this.subMenu == null && this.subMenuSupplier != null && selected) {
            this.subMenu = this.subMenuSupplier.get();
            this.subMenu.setParent(null);
            this.subMenu.setParentItem(this);
            this.subMenu.setPriority(Integer.MAX_VALUE);
            FormManager.getInstance().addContainer(this.subMenu);
        }
    }

    @Override
    public void render(SpriteBatch sb) {
        sb.setColor(Color.WHITE.cpy());
        if (this.bgTexture != null) {
            sb.draw(this.bgTexture, getX(true), getY(true), getWidth(), getHeight());
        }
        if (this.selected && this.selectedBgTexture != null) {
            sb.draw(this.selectedBgTexture, getX(true), getY(true), getWidth(), getHeight());
        }
        if (this.hasSplitLine && this.splitLineTexture != null) {
            sb.draw(this.splitLineTexture, getX(true) + getWidth() * 0.1F, getY(true), getWidth() * 0.8F, Math.max(1, getHeight() * 0.05F));
        }
        if (this.icon != null) {
            sb.draw(this.icon, getX(true), getY(true), this.iconWidth, this.iconWidth);
        }
        if (this.selected) {
            this.label.setColor(this.selectedTextColor);
            this.subMenuMarker.setColor(this.selectedTextColor);
        } else {
            this.label.setColor(this.textColor);
            this.subMenuMarker.setColor(this.textColor);
        }
        if (!this.enabled()) {
            this.label.setColor(this.disabledTextColor);
        }
        this.label.render(sb);
        if (this.subMenuSupplier != null) {
            this.subMenuMarker.render(sb);
        }
    }

    @Override
    public Container<? extends Element> getParent() {
        return parent;
    }

    @Override
    public void setParent(Container<? extends Element> parent) {
        this.parent = parent;
    }

    @Override
    public float getX(boolean isAbsolute) {
        return isAbsolute && getParent() != null ? getParent().getX(true) : 0;
    }

    @Override
    public float getY(boolean isAbsolute) {
        return isAbsolute && getParent() != null ? getParent().getY(true) + y : y;
    }

    @Override
    public void setX(float x) {
    }

    @Override
    public void setY(float y) {
        this.y = y;
    }

    @Override
    public float getWidth() {
        return getParent() != null ? getParent().getWidth() : this.width;
    }

    @Override
    public float getHeight() {
        return this.height;
    }

    @Override
    public void setWidth(float width) {
        this.width = width;
    }

    @Override
    public void setHeight(float height) {
        this.height = height;
    }

    @Override
    public Color getBackground() {
        return bgColor;
    }

    @Override
    public void setBackground(Color background) {
        if (!this.bgColor.equals(background)) {
            this.bgColor = background;
            refreshBgTexture();
        }
    }

    private void refreshBgTexture() {
        if (this.bgTexture != null) {
            this.bgTexture.dispose();
        }
        Pixmap pixmap = new Pixmap(1, 1, Pixmap.Format.RGBA8888);
        pixmap.setColor(this.bgColor);
        pixmap.fill();
        this.bgTexture = new Texture(pixmap);
    }

    public void setSelectedBgColor(Color selectedBgColor) {
        if (!this.selectedBgColor.equals(selectedBgColor)) {
            this.selectedBgColor = selectedBgColor;
            refreshSelectedBgTexture();
        }
    }

    private void refreshSelectedBgTexture() {
        if (this.selectedBgTexture != null) {
            this.selectedBgTexture.dispose();
        }
        Pixmap pixmap = new Pixmap(1, 1, Pixmap.Format.RGBA8888);
        pixmap.setColor(this.selectedBgColor);
        pixmap.fill();
        this.selectedBgTexture = new Texture(pixmap);
    }

    public void setSplitLineColor(Color splitLineColor) {
        if (!this.splitLineColor.equals(splitLineColor)) {
            this.splitLineColor = splitLineColor;
            refreshSplitLineTexture();
        }
    }

    private void refreshSplitLineTexture() {
        if (this.splitLineTexture != null) {
            this.splitLineTexture.dispose();
        }
        Pixmap pixmap = new Pixmap(1, 1, Pixmap.Format.RGBA8888);
        pixmap.setColor(this.splitLineColor);
        pixmap.fill();
        this.splitLineTexture = new Texture(pixmap);
    }

    public boolean hasSplitLine() {
        return hasSplitLine;
    }

    public void setSplitLine(boolean hasSplitLine) {
        this.hasSplitLine = hasSplitLine;
    }

    public void setTextColor(Color textColor) {
        this.textColor = textColor;
    }

    public void setDisabledTextColor(Color disabledTextColor) {
        this.disabledTextColor = disabledTextColor;
    }

    public void setSelectedTextColor(Color selectedTextColor) {
        this.selectedTextColor = selectedTextColor;
    }

    public void setIcon(Texture icon) {
        if (this.icon != null) {
            this.icon.dispose();
        }
        this.icon = icon;
    }

    public void setIconWidth(float iconWidth) {
        this.iconWidth = iconWidth;
    }

    public boolean isSelected() {
        return selected;
    }

    public void setSelected(boolean selected) {
        this.selected = selected;
    }

    GMenuList getSubMenuInstance() {
        return this.subMenu;
    }

    @Override
    public boolean isResizable() {
        return false;
    }

    @Override
    public void setResizable(boolean resizable) {

    }

    @Override
    public boolean enabled() {
        return enabled;
    }

    @Override
    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    @Override
    public boolean visible() {
        return visible;
    }

    @Override
    public void setVisible(boolean visible) {
        this.visible = visible;
    }

    @Override
    public void dispose() {
        if (this.bgTexture != null) {
            this.bgTexture.dispose();
        }
        if (this.selectedBgTexture != null) {
            this.selectedBgTexture.dispose();
        }
        if (this.splitLineTexture != null) {
            this.splitLineTexture.dispose();
        }
        if (this.icon != null) {
            this.icon.dispose();
        }
        this.label.dispose();
    }

    public boolean isHotKeyPressed() {
        return false;
    }

    @Override
    public boolean keyDown(int keyCode) {
        if (isHotKeyPressed()) {
            mouseDown = true;
            clickUp((int) Math.ceil(getX(true)), (int) Math.ceil(Gdx.graphics.getHeight() - getY(true)), 0);
        }
        return isHotKeyPressed();
    }

    @Override
    public boolean keyUp(int keyCode) {
        return false;
    }

    @Override
    public boolean keyTyped(char key) {
        return false;
    }

    @Override
    public boolean clickDown(int screenX, int screenY, int button) {
        if (!this.visible()) return false;
        mouseDown = screenX >= getX(true) && screenX <= getX(true) + getWidth() &&
                Gdx.graphics.getHeight() - screenY >= getY(true) && Gdx.graphics.getHeight() - screenY <= getY(true) + getHeight();
        return mouseDown;
    }

    @Override
    public boolean clickUp(int screenX, int screenY, int button) {
        if (!this.visible()) return false;
        boolean mouseUp = screenX >= getX(true) && screenX <= getX(true) + getWidth() &&
                Gdx.graphics.getHeight() - screenY >= getY(true) && Gdx.graphics.getHeight() - screenY <= getY(true) + getHeight();
        if (mouseDown && mouseUp) {
            if (enabled()) {
                onClick();
                Element parent = getParent();
                if (this.subMenuSupplier == null) {
                    while (parent instanceof GMenuList) {
                        GMenuList menuList = (GMenuList) parent;
                        if (menuList.getParentItem() == null || menuList.getParentItem().getParent() instanceof GMenuBar) {
                            menuList.disappear();
                            if (menuList.getParentItem() != null) {
                                if (menuList.getParentItem().getParent() instanceof GMenuBar) {
                                    menuList.getParentItem().setSelected(false);
                                }
                            }
                            break;
                        }
                        parent = menuList.getParentItem().getParent();
                    }
                }
            }
            mouseDown = false;
            return this.subMenuSupplier == null || !this.enabled();
        }
        return false;
    }

    public void onClick() {
    }

    @Override
    public boolean mouseDragged(int screenX, int screenY) {
        return false;
    }

    @Override
    public boolean mouseMoved(int screenX, int screenY) {
        selected = screenX >= getX(true) && screenX <= getX(true) + getWidth() &&
                Gdx.graphics.getHeight() - screenY >= getY(true) && Gdx.graphics.getHeight() - screenY <= getY(true) + getHeight() && enabled();
        return selected;
    }

    @Override
    public boolean scrolled(int amount) {
        return false;
    }

    @Override
    public boolean moveToElementBorder(Element element) {
        return false;
    }

    @Override
    public int getPriority() {
        return priority;
    }

    @Override
    public void setPriority(int priority) {
        this.priority = priority;
    }
}
